Android の Intent はどんな時に “同じ Intent” として扱われるか
概要
突然ですが、 Android に対して通知を発行するため等に使用する PendingIntent は、その一意性を Intent の一意性をもって定義しています。
このように、 Android 開発を行っていると何かと出くわすことの多い Intent が同一かどうかの判定 について解説します。
注: 実際には、 PendingIntent は IIntentSender を見ていて、そこに設定されている requestCode も一意性に含まれるのですが、ちょっと深く入りすぎるのでここでは"Intent を見ている"とさせて下さい。
Intent の同一性は Intent.filterEquals で確認される
ふたつの Intent が同一なものかどうか確認する際には、その確認元( AndroidSDK や OSS )によって Intent.filterEquals が呼びだされます。
このメソッドの定義および実装は次のとおりです。
public boolean filterEquals(Intent other) { if (other == null) { return false; } if (mAction != other.mAction) { if (mAction != null) { if (!mAction.equals(other.mAction)) { return false; } } else { if (!other.mAction.equals(mAction)) { return false; } } } if (mData != other.mData) { if (mData != null) { if (!mData.equals(other.mData)) { return false; } } else { if (!other.mData.equals(mData)) { return false; } } } if (mType != other.mType) { if (mType != null) { if (!mType.equals(other.mType)) { return false; } } else { if (!other.mType.equals(mType)) { return false; } } } if (mPackage != other.mPackage) { if (mPackage != null) { if (!mPackage.equals(other.mPackage)) { return false; } } else { if (!other.mPackage.equals(mPackage)) { return false; } } } if (mComponent != other.mComponent) { if (mComponent != null) { if (!mComponent.equals(other.mComponent)) { return false; } } else { if (!other.mComponent.equals(mComponent)) { return false; } } } if (mCategories != other.mCategories) { if (mCategories != null) { if (!mCategories.equals(other.mCategories)) { return false; } } else { if (!other.mCategories.equals(mCategories)) { return false; } } } return true; }
上記から、 Intent の一意性は次の要素によって定まっていることがわかります。
- mAction (setAction メソッドで設定できる String プロパティ)
- mData (setData メソッドで設定できる Uri プロパティ)
- mType (setType メソッドで設定できる String プロパティ)
- mPackage (setPackage メソッドで設定できる String プロパティ)
- mComponent (setComponent や、よく使用されるコンストラクタ で設定できる、 ComponentName プロパティ)※詳細は後述
- mCategories (addCategory メソッドで追加できる、Stringのセット)
このうち、とりわけ重要な要素となっているのが、 mComponent プロパティです。
まずは、 Intent クラスに定義された次のコンストラクタ定義を確認してください。
public Intent(Context packageContext, Class<?> cls) { mComponent = new ComponentName(packageContext, cls); }
これは、私たちが普段 Intent intent = new Intent(this, MyReallyCoolActiviy.class); といった具合に使用しているコンストラクタです。
上記のコンストラクタでは、 mComponent 以外の値が設定されていないことがわかります。
つまり、 私たちが別途 intent.setAction("MY_AWESOME_ACTION"); や intent.setData(Uri.parse("custom:uri")); のような設定をしない限りは、次の2つの Intent は同一です:
Intent myAwesomeIntent = new Intent(this, HogeActivity.class); Intent myCoolIntent = new Intent(this, HogeActivity.class); Log.d(TAG, "filterEquals: " + myAwesomeIntent.filterEquals(myCoolIntent)); // -> true
そして、上述の仕様を把握すれば、 Intent に任意の一意性を持たせることが出来ます。
Intent に任意の一意性を付加する
概要で説明したとおり、 Android 開発では何かと Intent が同じかどうかが問題になることがあります。
一例ですが、通常のアクティビティ呼び出しや、サービス呼び出しに使用する Intent にであれば、次のように任意に一意性を与えることが出来ます。
public static Intent createIntentWithId(String intentId) { Intent intent = new Intent(this, HogeActiviy.class); intent.setData(Uri.parse("myApp://intentId/" + intentId)); return intent; }
(前述のとおり)上の例で使用しているコンストラクタは mComponent のみを設定します。よって、他の項目については私たちが設定できます。
上の例では、 mData に値を設定することで一意性を付加しています。
setType(String type) メソッドは、 mType
を設定するだけでなく、 mData に null を設定します。このメソッド使用する際には注意してください。
まとめ
Intent の filterEquals の仕様を理解して、うまく Intent を管理しましょう。